package org.teamswift.crow.rbac.security.realms;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.teamswift.crow.rbac.dto.CrowUserFromJWTPayloads;
import org.teamswift.crow.rbac.entity.CrowRole;
import org.teamswift.crow.rbac.entity.CrowUser;
import org.teamswift.crow.rbac.enums.CrowRbacErrors;
import org.teamswift.crow.rbac.exceptions.CredentialInvalidException;
import org.teamswift.crow.rbac.security.CrowSecurityUtils;
import org.teamswift.crow.rbac.security.token.CrowJWTToken;
import org.teamswift.crow.rbac.service.CrowUserService;
import org.teamswift.crow.rest.utils.CrowBeanUtils;
import org.teamswift.crow.rest.utils.CrowMessageUtil;

import java.util.Set;
import java.util.stream.Collectors;

public class CrowJWTRealm extends AuthorizingRealm {

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof CrowJWTToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        CrowUser crowUser = (CrowUser) SecurityUtils.getSubject();

        CrowUserService crowUserService = CrowBeanUtils.getBean(CrowUserService.class);
        Set<CrowRole> roles = crowUserService.getRoles(crowUser);
        Set<Permission> permissionResources = crowUserService.getPermissions(crowUser);

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles.stream().map(CrowRole::getName).collect(Collectors.toSet()));
        authorizationInfo.setObjectPermissions(permissionResources);

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        CrowJWTToken token = (CrowJWTToken) authenticationToken;

        CrowSecurityUtils crowSecurityUtils = CrowBeanUtils.getBean(CrowSecurityUtils.class);

        CrowUserFromJWTPayloads tokenInfo = crowSecurityUtils.read(token.getToken());
        CrowUserService crowUserService = CrowBeanUtils.getBean(CrowUserService.class);

        CrowUser user = crowUserService.findOneById(tokenInfo.getId());

        if(user == null || !user.getUsername().equals(tokenInfo.getUsername())) {
            throw new CredentialInvalidException(
                    CrowMessageUtil.error(CrowRbacErrors.CredentialInvalid)
            );
        }

        return new SimpleAuthenticationInfo(
                user, token.getToken(),
                user.getName()
        );
    }
}
